home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue31 / bitview / BITVIEW.ZIP / BITVIEW.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1998-01-30  |  15.3 KB  |  512 lines

  1. //=========================== *BIG* BITMAP VIEWER ==============================
  2. //
  3. // This component came about because I wanted to display 4000 x 4000 x 256 colour
  4. // bitmaps (about 16MB in size).  Using a TBitmap and a TImage took ages to load
  5. // the images as a whole load of disc-swap file activity took place.  The answer
  6. // was to use a memory mapped file and the StretchDIBits API call which takes
  7. // a memory pointer to the bitmap data, and doesn't realise (of course) that
  8. // it's a memory mapped file.  Load times and resource used drastically reduced.
  9. //
  10. // This component was written to display the UK Ordanance Survey's 1:50000 scale
  11. // Landranger maps bought as bitmaps.  If anyone wants some additional code
  12. // associated with displaying these maps then contact me at the email below.
  13. //
  14. // This version has been used and tested on 2, 16 and 256 colour bitmaps.  I
  15. // believe it should work on the higher resolution bitmaps as well, but it
  16. // is untested.
  17. //
  18. // The component is a desendant of TGraphicControl (just like a TPaintBox). I
  19. // used the TGraphicControl rather than a TPaintBox to have control of the
  20. // parent properties I wanted to publish.
  21. //
  22. // Version 1.00
  23. // Grahame Marsh 19 January 1997
  24. //
  25. // Freeware - you get it for free, I take nothing, I make no promises!
  26. //
  27. // Please feel free to contact me: grahame.s.marsh@courtaulds.com
  28. //
  29. // Revison History:
  30. //    Version 1.00 - initial release  19-1-97
  31. //            1.01 - improved colour rendering under NT 4 using SetStretchBltMode
  32. //                   changed name GetPalette to GetBitmapPalette
  33.  
  34. unit
  35.   BitView;
  36.  
  37. interface
  38.  
  39. uses
  40.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, DsgnIntf;
  41.  
  42. type
  43.   TBMPFilename = type string; // filename will have its owner property editor
  44.                               // Is this use for "type" documented anywhere?
  45. type
  46.   TBigBitmapViewer = class (TGraphicControl)
  47.   private
  48.     FFileName: TBMPFilename;  // bitmap filename, own type so it can have own prop editor
  49.     FPalette : HPalette;      // handle to bitmap palette
  50.     FData    : pointer;       // pointer to start of data in memory mapped file
  51.     FBitmapWidth,             // copy of bitmap width info for convience
  52.     FBitmapHeight,            // copy of bitmap height info for convience
  53.     FColours : integer;       // number of colours in palette
  54.     FCentre,                  // centre the bitmap in the control
  55.     FStretch,                 // stretch the bitmap to fill the control
  56.     FAutoSize,                // automatically size the control to display the bitmap
  57.     FActive  : boolean;       // true opens the viewer, false it's closed
  58.     FFileHeader : PBitmapFileHeader;  // pointer to TBitmapFileHeader record
  59.     FInfoHeader : PBitmapInfoHeader;  // pointer to TBitmapInfoHeader record
  60.     FInfo : PBitmapInfo;      // pointer to the TBitmapInfo record
  61.     FPixelStart : pointer;    // pointer to the start of the pixel data
  62.     procedure SetActive (Value : boolean);
  63.     procedure SetAutoSize (Value : boolean);
  64.     procedure SetFilename (const Value : TBMPFilename);
  65.     procedure SetStretch (Value : boolean);
  66.     procedure SetCentre (Value : boolean);
  67.     procedure SetDummyInt (Value : integer);
  68.   protected
  69.     procedure OpenViewer; virtual;
  70.     procedure CloseViewer; virtual;
  71.     procedure GetBitmapPalette; virtual;
  72.     procedure Paint; override; // virtual
  73.     procedure Changes; virtual;
  74.   public
  75.     constructor Create (AOwner : TComponent); override;
  76.     destructor Destroy; override;
  77. // open the viewer
  78.     procedure Close;
  79. // close the viewer
  80.     procedure Open;
  81. // pointer to the file header info
  82.     property BitmapFileHeader : PBitmapFileHeader read FFileHeader;
  83. // pointer to the bitmap info header
  84.     property BitmapInfoHeader : PBitmapInfoHeader read FInfoHeader;
  85. // pointer to the bitmap info
  86.     property BitmapInfo : PBitmapInfo read FInfo;
  87. // pointer to the bitmap pixel data array
  88.     property PixelStart : pointer read FPixelStart;
  89. // palette handle
  90.     property Palette : HPalette read FPalette;
  91.   published
  92. //READ-WRITE PROPS
  93. // size control to bitmap
  94.     property AutoSize : boolean read FAutoSize write SetAutoSize default false;
  95. // bitmap centred
  96.     property Centre : boolean read FCentre write SetCentre default false;
  97. // filename of bitmap
  98.     property Filename : TBMPFilename read FFilename write SetFilename;
  99. // stretch bitmap
  100.     property Stretch : boolean read FStretch write SetStretch default false;
  101. // READ-ONLY PROPS
  102. // number of colours in the bitmap palette
  103.     property Colours : integer read FColours write SetDummyInt stored false;
  104. // bitmap width
  105.     property BitmapHeight : integer read FBitmapHeight write SetDummyInt stored false;
  106. // bitmap height
  107.     property BitmapWidth : integer read FBitmapWidth write SetDummyInt stored false;
  108. // TGraphicControl PROPS NOW PUBLISHED
  109.     property Align;
  110.     property DragCursor;
  111.     property DragMode;
  112.     property Enabled;
  113.     property ParentShowHint;
  114.     property PopupMenu;
  115.     property ShowHint;
  116.     property Visible;
  117.     property OnClick;
  118.     property OnDblClick;
  119.     property OnDragDrop;
  120.     property OnDragOver;
  121.     property OnEndDrag;
  122.     property OnMouseDown;
  123.     property OnMouseMove;
  124.     property OnMouseUp;
  125.     property OnStartDrag;
  126. // viewer activate - stream active last!
  127.     property Active : boolean read FActive write SetActive default false;
  128.   end;
  129.  
  130. procedure Register;
  131.  
  132. implementation
  133.  
  134. const
  135.   BitmapSignature = $4D42;
  136.  
  137. procedure InvalidBitmap;
  138. begin
  139.   raise Exception.Create ('Bitmap image is not valid')
  140. end;
  141.  
  142. procedure NotWhenActive;
  143. begin
  144.   raise Exception.Create ('Not on an active big bitmap viewer')
  145. end;
  146.  
  147. constructor TBigBitmapViewer.Create (AOwner : TComponent);
  148. begin
  149.   inherited Create (AOwner);
  150.   Width := 150;
  151.   Height := 150
  152. end;
  153.  
  154. destructor TBigBitmapViewer.Destroy;
  155. begin
  156.   CloseViewer;  // ensure file view is freed
  157.   inherited Destroy
  158. end;
  159.  
  160. // This procedure takes the palette out of the bitmap.  It references two
  161. // values to do this (FColours - the colour count, and FInfo a pointer to
  162. // TBitmapInfo record).  This is a fairly standard way of getting a palette.
  163. // If successful, FPalette contains a handle to a copy of the palette.
  164. procedure TBigBitmapViewer.GetBitmapPalette;
  165. var
  166.   SysPalSize,
  167.   Loop,
  168.   LogSize : integer;
  169.   LogPalette : PLogPalette;
  170.   DC : HDC;
  171.   Focus : HWND;
  172. begin
  173.   FPalette := 0;
  174.  
  175. // fetch palette for colour bitmaps only
  176.   if FColours > 2 then
  177.   begin
  178.  
  179. // create palette from bitmap info
  180.     LogSize := SizeOf (TLogPalette) + pred(FColours) * SizeOf(TPaletteEntry);
  181.     LogPalette := AllocMem (LogSize);
  182.     try
  183.       with LogPalette^ do
  184.       begin
  185.         palNumEntries := FColours;
  186.         palVersion := $0300;
  187.  
  188. // I prefer to test programs with $R+, but this section of the program
  189. // must be compiled with $R-.  This $IFOPT enables the restoration of
  190. // $R+ condition later on, but only if set now.
  191. {$IFOPT R+}
  192.   {$DEFINE R_PLUS}
  193.   {$R-}
  194. {$ENDIF}
  195.         Focus := GetFocus;
  196.         DC := GetDC (Focus);
  197.         try
  198.           SysPalSize := GetDeviceCaps (DC, SIZEPALETTE);
  199.           if (FColours = 16) and (SysPalSize >= 16) then
  200.           begin
  201.             GetSystemPaletteEntries (DC, 0, 8, palPalEntry);
  202.             loop := 8;
  203.             GetSystemPaletteEntries (DC, SysPalSize - loop, loop, palPalEntry[loop])
  204.           end else
  205.             with FInfo^ do
  206.               for loop := 0 to pred (FColours) do
  207.               begin
  208.                 palPalEntry[loop].peRed   := bmiColors[loop].rgbRed;
  209.                 palPalEntry[loop].peGreen := bmiColors[loop].rgbGreen;
  210.                 palPalEntry[loop].peBlue  := bmiColors[loop].rgbBlue
  211.               end
  212.         finally
  213.           ReleaseDC(Focus, DC)
  214.         end
  215. {$IFDEF R_PLUS}
  216.   {$R+}
  217.   {$UNDEF R_PLUS}
  218. {$ENDIF}
  219.       end;
  220.       FPalette := CreatePalette (LogPalette^)
  221.     finally
  222.       FreeMem (LogPalette, LogSize)
  223.     end
  224.   end
  225. end;
  226.  
  227. // Open the file for viewing, here is the memory mapped file stuff.  It
  228. // is a simple use of memory mapped files as I only need to open the file
  229. // with read permissions.  The following steps are taken.
  230. // 1. Get a read only file handle to the bitmap file
  231. // 2. Get a file mapping using that handle
  232. // 3. Get a view of the file from that handle
  233. // 4. Set up some useful pointers to often used bits of the view
  234. // 5. Get the palette (if present) from the view
  235.  
  236. procedure TBigBitmapViewer.OpenViewer;
  237. var
  238.   FileHandle,
  239.   MapHandle : THandle;
  240. begin
  241.   if FActive then
  242.     exit;
  243.  
  244. // open file
  245.   FileHandle := FileOpen (FFilename, fmOpenRead + fmShareDenyNone);
  246.   if FileHandle = INVALID_HANDLE_VALUE then
  247.     raise Exception.Create ('Failed to open ' + FFilename);
  248.  
  249. // create file map and throw away the file handle
  250.   try
  251.     MapHandle := CreateFileMapping (FileHandle, nil, PAGE_READONLY, 0, 0, nil);
  252.     if MapHandle = 0 then
  253.       raise Exception.Create ('Failed to map file')
  254.   finally
  255.     CloseHandle (FileHandle)
  256.   end;
  257.  
  258. // view file map and throw away the map handle
  259.   try
  260.     FData := MapViewOfFile (MapHandle, FILE_MAP_READ, 0, 0, 0);
  261.     if FData = nil then
  262.       raise Exception.Create ('Failed to view map file')
  263.   finally
  264.     CloseHandle (MapHandle)
  265.   end;
  266.  
  267. // set pointers into file view
  268.   FFileHeader := FData;
  269.  
  270. // test for valid bitmap file:
  271.   if FFileHeader^.bfType <> BitmapSignature then
  272.   begin
  273.     UnmapViewOfFile (FData);
  274.     FData := nil;
  275.     InvalidBitmap
  276.   end;
  277.  
  278. // set up a few other pointers into the data for records we will
  279. // need to reference in the file, it's easiest just to do it this once.
  280.   FInfoHeader := pointer (integer (FData) + sizeof (TBitmapFileHeader));
  281.   FInfo := pointer (FInfoHeader);
  282.   FPixelStart := pointer (integer(FData) + FFileHeader^.bfOffBits);
  283.  
  284. // get number of colours, above 256 colour files have FColours=0
  285.   with FInfoHeader^ do
  286.     if biClrUsed <> 0 then
  287.       FColours := biClrUsed
  288.     else
  289.       case biBitCount of
  290.         1,
  291.         4,
  292.         8 : FColours := 1 shl biBitCount
  293.       else
  294.         FColours := 0
  295.       end;
  296.  
  297.   // get bitmap size into easy to access properties
  298.   FBitmapHeight := FInfoHeader^.biHeight;
  299.   FBitmapWidth := FInfoHeader^.biWidth;
  300.  
  301. // fetch the palette
  302.   GetBitmapPalette;
  303.  
  304. // other setups
  305.   FActive := true;
  306.   Changes
  307. end;
  308.  
  309. // The viewer PAINT method.
  310. //            -----
  311. // The actions carried out here are:
  312. // 1. If in design mode and not active put up a simple rectangle
  313. // 2. Select and realize the bitmap palette
  314. // 3. Calculate the bitmap image location taking into account the stretch
  315. //    and centre properties
  316. // 4. Squirt the bitmap onto the canvas
  317. // 5. Select the orginal palette
  318.  
  319. procedure TBigBitmapViewer.Paint;
  320. var
  321.   OldMode    : integer;
  322.   OldPalette : HPalette;
  323.   Dest       : TRect;
  324. begin
  325.   with Canvas do
  326. // simple rectangle for design mode
  327.     if (csDesigning in ComponentState) and not FActive then
  328.     begin
  329.       Pen.Style := psDash;
  330.       Brush.Style := bsClear;
  331.       Rectangle (0, 0, Width, Height)
  332.     end else begin
  333. // select the bitmap palette
  334.       if FPalette <> 0 then
  335.         OldPalette := SelectPalette (Handle, FPalette, false)
  336.       else
  337.         OldPalette := 0;
  338.  
  339.       try
  340.         RealizePalette (Handle);
  341. // calculate the bitmap location
  342.         if FStretch then
  343.           Dest := ClientRect
  344.         else
  345.           if Centre then
  346.             Dest := Rect ((Width - FBitmapWidth) div 2, (Height - FBitmapHeight) div 2,
  347.                             FBitmapWidth, FBitmapHeight)
  348.           else
  349.             Dest := Rect (0, 0, FBitmapWidth, FBitmapHeight);
  350.  
  351. // set blt mode according to number of colours
  352.         if FColours = 2 then
  353.           OldMode := SetStretchBltMode (Handle, BLACKONWHITE)
  354.         else
  355.           OldMode := SetStretchBltMode (Handle, COLORONCOLOR);
  356.         try
  357. // display it
  358.           with Dest do
  359.             StretchDIBits (Handle,
  360.                            Left, Top, Right, Bottom,
  361.                            0, 0, FBitmapWidth, FBitmapHeight,
  362.                            FPixelStart, FInfo^,
  363.                            DIB_RGB_COLORS, SRCCOPY)
  364.         finally
  365.           SetStretchBltMode (Handle, OldMode)
  366.         end
  367.       finally
  368. // put the old palette back in
  369.         if OldPalette <> 0 then
  370.           SelectPalette (Handle, OldPalette, false)
  371.       end
  372.     end
  373. end;
  374.  
  375. // close the viewer by unmapping the file, setting the view to nil and
  376. // discarding the palette
  377. procedure TBigBitmapViewer.CloseViewer;
  378. begin
  379.   if FActive then
  380.   begin
  381.     FActive := false;
  382.     if FData <> nil then
  383.     begin
  384.       UnmapViewOfFile (FData);  //  remove the memory mapped file view
  385.       FData := nil
  386.     end;
  387.     if FPalette <> 0 then
  388.       DeleteObject (FPalette)   // free the palette
  389.   end
  390. end;
  391.  
  392. // set active to true
  393. procedure TBigBitmapViewer.Open;
  394. begin
  395.   Active := true
  396. end;
  397.  
  398. // set active to false
  399. procedure TBigBitmapViewer.Close;
  400. begin
  401.   Active := false
  402. end;
  403.  
  404. // Property Methods:
  405.  
  406. procedure TBigBitmapViewer.SetActive (Value : boolean);
  407. begin
  408.   if Value <> FActive then
  409.     if Value then
  410.       OpenViewer
  411.     else
  412.       CloseViewer
  413. end;
  414.  
  415. procedure TBigBitmapViewer.SetAutoSize (Value : boolean);
  416. begin
  417.   if Value <> FAutoSize then
  418.   begin
  419.     FAutoSize := Value;
  420.     Changes
  421.   end
  422. end;
  423.  
  424. procedure TBigBitmapViewer.SetStretch (Value : boolean);
  425. begin
  426.   if Value <> FStretch then
  427.   begin
  428.     FStretch := Value;
  429.     Changes
  430.   end
  431. end;
  432.  
  433. procedure TBigBitmapViewer.SetCentre (Value : boolean);
  434. begin
  435.   if Value <> FCentre then
  436.   begin
  437.     FCentre := Value;
  438.     Changes
  439.   end
  440. end;
  441.  
  442. procedure TBigBitmapViewer.SetFilename (const Value : TBMPFilename);
  443. begin
  444.   if Value <> FFilename then
  445.   begin
  446.     if FActive then
  447.       NotWhenActive;
  448.     FFilename := Value
  449.   end
  450. end;
  451.  
  452. // This dummy set integer procedure is used with the BitmapHeight and
  453. // BitmapWidth properties to make them appear in the object inspector
  454. // WITHOUT allowing them to be edited - ie readonly.
  455. procedure TBigBitmapViewer.SetDummyInt (Value : integer);
  456. begin
  457. end;
  458.  
  459. // Process changes to the TGraphicControl depending on size of
  460. // the image compared to the form.
  461. procedure TBigBitmapViewer.Changes;
  462. begin
  463.   if (BitmapWidth >= Width) and (BitmapHeight >= Height) then
  464.     ControlStyle := ControlStyle + [csOpaque]
  465.   else
  466.     ControlStyle := ControlStyle - [csOpaque];
  467.  
  468.   if AutoSize and (BitmapWidth > 0) and (BitmapHeight > 0) then
  469.     SetBounds (Left, Top, BitmapWidth, BitmapHeight)
  470.   else
  471.     Invalidate
  472. end;
  473.  
  474. // filename property editor .. fileopen dialog box
  475. type
  476.   TBMPFilenameProperty = class (TStringProperty)
  477.   public
  478.     procedure Edit; override;
  479.     function GetAttributes: TPropertyAttributes; override;
  480.   end;
  481.  
  482. procedure TBMPFilenameProperty.Edit;
  483. begin
  484.   with TOpenDialog.Create(Application) do
  485.   begin
  486.     Filename := GetValue;
  487.     Filter := 'Windows bitmaps (*.BMP)|*.BMP';
  488.     Options := Options + [ofPathMustExist, ofFileMustExist, ofHideReadOnly];
  489.     try
  490.       if Execute then
  491.         SetValue(Filename)
  492.     finally
  493.       Free
  494.     end
  495.   end
  496. end;
  497.  
  498. function TBMPFilenameProperty.GetAttributes: TPropertyAttributes;
  499. begin
  500.   Result := [paDialog, paRevertable]
  501. end;
  502.  
  503. procedure Register;
  504. begin
  505.   RegisterComponents ('My Controls', [TBigBitmapViewer]);
  506.   RegisterPropertyEditor (TypeInfo (TBMPFilename), nil, '', TBMPFilenameProperty);
  507. end;
  508.  
  509. end.
  510.  
  511.  
  512.